home *** CD-ROM | disk | FTP | other *** search
/ Games of Daze / Infomagic - Games of Daze (Summer 1995) (Disc 1 of 2).iso / x2ftp / msdos / misc / wt004wc / render.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-05-27  |  36.5 KB  |  1,245 lines

  1. /*
  2. **  wt -- a 3d game engine
  3. **
  4. **  Copyright (C) 1994 by Chris Laurel
  5. **  email:  claurel@mr.net
  6. **  snail mail:  Chris Laurel, 5700 W Lake St #208,  St. Louis Park, MN  55416
  7. **
  8. **  This program is free software; you can redistribute it and/or modify
  9. **  it under the terms of the GNU General Public License as published by
  10. **  the Free Software Foundation; either version 2 of the License, or
  11. **  (at your option) any later version.
  12. **
  13. **  This program is distributed in the hope that it will be useful,
  14. **  but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. **  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16. **  GNU General Public License for more details.
  17. **
  18. **  You should have received a copy of the GNU General Public License
  19. **  along with this program; if not, write to the Free Software
  20. **  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  21. */
  22.  
  23. /* Some minor changes made for the MS-DOS port
  24.  * by Petteri Kangaslampi <pekanga@freeport.uwasa.fi>
  25. */
  26.  
  27. #define PROFILE 0
  28.  
  29. /* Note! PROFILE MUST be 0 with MS-DOS systems (PK) */
  30.  
  31. #include <math.h>
  32. #include <stdlib.h>
  33. #include <string.h>
  34.  
  35. #if PROFILE
  36. #include <sys/types.h>
  37. #include <unistd.h>
  38. #include <sys/time.h>
  39. #endif
  40.  
  41. #include <stdio.h>                      /* stdio.h is needed even if */
  42. #include "wt.h"                         /* PROFILE is 0. Fixed. (PK) */
  43. #include "error.h"
  44. #include "fixed.h"
  45. #include "wtmem.h"
  46. #include "table.h"
  47. #include "view.h"
  48. #include "texture.h"
  49. #include "framebuf.h"
  50. #include "graphics.h"
  51. #include "world.h"
  52. #include "render.h"
  53.  
  54.  
  55. #define VIEW_WIDTH  (fb->fb_width)
  56. #define VIEW_HEIGHT (fb->fb_height)
  57.  
  58. /* These macros convert 16.16 fixed point numbers to and from a 2.30 format.
  59. **   The extra fractional precision is needed when doing doing 1 / distance
  60. **   calculations for perspective.
  61. */
  62. #define TO_FIX_2_30(f)   ((f) << 14)
  63. #define FROM_FIX_2_30(f) ((f) >> 14)
  64. #define TO_FIX_8_24(f)   ((f) << 8)
  65. #define FROM_FIX_8_24(f) ((f) >> 8)
  66. #define MAX_WALL_EVENTS 30
  67.  
  68. #define MAX_ERROR  SCREEN_WIDTH
  69.  
  70.  
  71. typedef struct {
  72.      Wall *wall;
  73.      fixed z, dz;
  74.      Boolean is_back_view;
  75. } Wall_start;
  76.  
  77. typedef Wall *Wall_end;
  78.  
  79. typedef struct {
  80.      int n_events;
  81.      Wall_start events[MAX_WALL_EVENTS];
  82. } Wall_start_list;
  83.  
  84. typedef struct {
  85.      int n_events;
  86.      Wall_end events[MAX_WALL_EVENTS];
  87. } Wall_end_list;
  88.  
  89. typedef struct {
  90.      Boolean is_back_view;
  91.      Wall *wall;
  92.      Boolean visible;
  93.      fixed pstart1, pend1, pstart2, pend2;
  94.      fixed dpstart1, dpend1, dpstart2, dpend2;
  95.      fixed z;
  96.      fixed dz;
  97. } Active_wall;
  98.  
  99. typedef struct {
  100.      fixed pstart1, pend1, pstart2, pend2;
  101.      Boolean is_back_view;
  102.      fixed top, bottom;
  103.      Region *front, *back;
  104.      Wall *wall;
  105.      fixed z;
  106. } Wall_intersection;
  107.  
  108. typedef struct {
  109.      fixed screen_dy, screen_dx;
  110.      fixed view_sin, view_cos;
  111.      fixed sin_dx, cos_dx;
  112.      fixed *sin_tab, *cos_tab, *row_view;
  113. } View_constants;
  114.  
  115.  
  116. static void transform_vertices(World *w, View *view);
  117. static void clip_walls(World *w, View *v);
  118. static void add_wall_events(View *v, Wall *wall,
  119.                 fixed x1, fixed px1, fixed x2, fixed px2);
  120. static void render_walls(World *w, View *v);
  121. static int add_events(Active_wall *active, int n_active, int column);
  122. static Boolean wall_obscured(Vertex *common, Vertex *v1, Vertex *v2);
  123. static int remove_events(Active_wall *active, int n_active, int column);
  124. static Wall_intersection *calc_wall_heights(Active_wall *active, int n_active,
  125.                         int column, fixed height,
  126.                         Wall_intersection *cur_slice);
  127. static void draw_walls(Wall_intersection *int_list, View *v);
  128. static void draw_floors(Wall_intersection *int_list, View *v);
  129. static void draw_floor_slices(Region *r, fixed start, fixed end, View *v,
  130.                   int column);
  131. static void draw_ceiling_slices(Region *r, fixed start, fixed end,
  132.                 View *v, int column);
  133. static fixed wall_ray_intersection(fixed Vx, fixed Vy, Wall *wall);
  134. static void init_buffers(void);
  135. static void calc_view_constants(View *v, int screen_width, int screen_height);
  136.  
  137.  
  138. static Framebuffer *fb = NULL;
  139. static Wall_start_list *start_events = NULL;
  140. static Wall_end_list *end_events = NULL;
  141. static Wall_intersection *intersections = NULL;
  142. static int *fb_rows = NULL;
  143. static View_constants view_constants;
  144.  
  145.  
  146. #if defined(__GNUC__) && defined(ARCH_i86)
  147. #include "slice-gas86.c"
  148. #else
  149. #ifdef __WATCOMC__
  150. #include "slice-wc.c"                   /* Watcom C specific scaling
  151.                                            routines */
  152. #else
  153. #include "slice.c"
  154. #endif
  155. #endif
  156.  
  157.  
  158.  
  159. void init_renderer(int fb_width, int fb_height)
  160. {
  161.      int i;
  162.  
  163.  
  164.      if (fb != NULL)
  165.       wtfree(fb);
  166.      if (start_events != NULL)
  167.       wtfree(start_events);
  168.      if (end_events != NULL)
  169.       wtfree(end_events);
  170.      if (fb_rows != NULL)
  171.       wtfree(fb_rows);
  172.  
  173.      fb = new_framebuffer(fb_width, fb_height);
  174.      start_events = wtmalloc(sizeof(Wall_start_list) * (fb_width + 1));
  175.      end_events = wtmalloc(sizeof(Wall_end_list) * (fb_width + 1));
  176.      fb_rows = wtmalloc(sizeof(int) * fb_height);
  177.      for (i = 0; i < fb_height; i++)
  178.       fb_rows[i] = i * fb_width;
  179. }
  180.  
  181.  
  182. #if PROFILE
  183. static double current_time(void)
  184. {
  185.      struct timeval tv;
  186.      struct timezone tz;
  187.  
  188.      tz.tz_minuteswest = 0;
  189.      tz.tz_dsttime = DST_NONE;
  190.      gettimeofday(&tv, &tz);
  191.  
  192.      return (double) tv.tv_sec + (double) tv.tv_usec / 1000000.0;
  193. }
  194. #endif
  195.  
  196. #ifdef ARCH_SUN
  197. static void *memmove(void *dest, void *src, size_t size)
  198. {
  199.      int *lsrc = (int *) src;
  200.      int *ldest = (int *) dest;
  201.  
  202.      size /= 4;
  203.      /* Here, we assume that size is a multiple of four.  That's the only
  204.      **   way in which memmove will be used.  This assumption is nasty,
  205.      **   but life is rough . . . I'm bitter about having to implement
  206.      **   memmove in the first place.
  207.      */
  208.      if (lsrc < ldest) {
  209.       ldest += size - 1;
  210.       lsrc += size - 1;
  211.       while (size-- > 0)
  212.            *ldest-- = *lsrc--;
  213.      } else {
  214.       while (size-- > 0)
  215.            *ldest++ = *lsrc++;
  216.      }
  217.  
  218.      return dest;
  219. }
  220. #endif
  221.  
  222.  
  223. void render(World *w, View *v)
  224. {
  225. #if PROFILE
  226.      static int fps_count = 0;
  227.      static double total_time = 0.0;
  228.      double start_time = current_time();
  229. #endif
  230.      init_buffers();
  231.      calc_view_constants(v, VIEW_WIDTH, VIEW_HEIGHT);
  232.      transform_vertices(w, v);
  233.      clip_walls(w, v);
  234.      clear_framebuffer(fb);
  235.      render_walls(w, v);
  236.      update_screen(fb);
  237.  
  238. #if PROFILE
  239.      fps_count++;
  240.      total_time += current_time() - start_time;
  241.      if (fps_count == 100) {
  242.       printf("fps = %3.2f\n", (double) fps_count / total_time);
  243.       fps_count = 0;
  244.       total_time = 0.0;
  245.      }
  246. #endif
  247. }
  248.  
  249.  
  250. static void transform_vertices(World *w, View *view)
  251. {
  252.      Vertex *vertex;
  253.      fixed view_sin, view_cos;
  254.      int i;
  255.  
  256.  
  257.      vertex = TABLE_ELEMENTS(w->vertices, Vertex);
  258.      view_sin = view_constants.view_sin;
  259.      view_cos = view_constants.view_cos;
  260.  
  261.      for (i = 0; i < TABLE_SIZE(w->vertices); i++, vertex++) {
  262.       fixed x = vertex->x - view->x;
  263.       fixed y = vertex->y - view->y;
  264.  
  265.       vertex->tx = fixmul(x, view_cos) - fixmul(y, view_sin);
  266.       vertex->ty = fixmul(x, view_sin) + fixmul(y, view_cos);
  267.       if (vertex->tx > view->eye_distance)
  268.            /* project point onto view plane */
  269.            vertex->proj = fixdiv(vertex->ty, vertex->tx);
  270.      }
  271. }
  272.  
  273.  
  274. static void clip_walls(World *w, View *v)
  275. {
  276.      Wall *wall = (Wall *) w->walls->table;
  277.      int i;
  278.  
  279.      for (i = 0; i < TABLE_SIZE(w->walls); i++, wall++) {
  280.       fixed x1, y1, px1, x2, y2, px2;
  281.       unsigned int outcode1, outcode2;
  282.  
  283.       x1 = wall->vertex1->tx;
  284.       x2 = wall->vertex2->tx;
  285.       /* See if the wall lies completely behid the view plane. */
  286.       if (x1 < v->eye_distance && x2 < v->eye_distance)
  287.            continue;
  288.  
  289.       y1 = wall->vertex1->ty;
  290.       px1 = wall->vertex1->proj;
  291.       y2 = wall->vertex2->ty;
  292.       px2 = wall->vertex2->proj;
  293.  
  294.       /*** Clipping ***/
  295.  
  296.          /* First, clip to the view plane (line, really, since we're
  297.       **   working in only two dimensions.)
  298.       */
  299.       if (x1 <= v->eye_distance) {
  300.            /* be careful for division overflow */
  301.            if (x2 - x1 < FIXED_EPSILON)
  302.             continue;
  303.            y1 = y1 + fixmul(v->eye_distance - x1,
  304.                 fixdiv(y2 - y1, x2 - x1));
  305.            px1 = y1;
  306.            x1 = v->eye_distance;
  307.       }
  308.       if (x2 <= v->eye_distance) {
  309.            if (x1 - x2 < FIXED_EPSILON)
  310.             continue;
  311.            y2 = y2 + fixmul(v->eye_distance - x2,
  312.                 fixdiv(y1 - y2, x1 - x2));
  313.            px2 = y2;
  314.            x2 = v->eye_distance;
  315.       }
  316.  
  317.       /* Now, clip to the sides of the view polygon. */
  318.       outcode1 = FIXED_SIGN(v->view_plane_size + px1);
  319.       outcode1 |= FIXED_SIGN(v->view_plane_size - px1) << 1;
  320.       outcode2 = FIXED_SIGN(v->view_plane_size + px2);
  321.       outcode2 |= FIXED_SIGN(v->view_plane_size - px2) << 1;
  322.  
  323.       /* trivial reject */
  324.       if ((outcode1 & outcode2) != 0)
  325.            continue;
  326.       /* check for trivial accept */
  327.       if ((outcode1 | outcode2) != 0) {
  328.            /* Damn . . . we need to clip. */
  329.            fixed slope, denom, y_diff;
  330.  
  331.            denom = (x2 - x1);
  332.            if (FIXED_ABS(denom) < FIXED_EPSILON) {
  333.             if (denom < 0)
  334.              slope = FIXED_MIN + v->view_plane_size;
  335.             else
  336.              slope = FIXED_MAX - v->view_plane_size;
  337.            } else
  338.             slope = fixdiv(y2 - y1, denom);
  339.  
  340.            if (outcode1 == 1) {
  341.             px1 = -v->view_plane_size;
  342.             y_diff = y1 - fixmul(x1, -v->view_plane_size);
  343.             slope += v->view_plane_size;
  344.             if (FIXED_ABS(slope) > FIXED_EPSILON)
  345.              x1 -= fixdiv(y_diff, slope);
  346.             else
  347.              x1 = FIXED_MAX;
  348.            } else if (outcode1 == 2) {
  349.             px1 = v->view_plane_size;
  350.             y_diff = y1 - fixmul(x1, v->view_plane_size);
  351.             slope -= v->view_plane_size;
  352.             if (FIXED_ABS(slope) > FIXED_EPSILON)
  353.              x1 -= fixdiv(y_diff, slope);
  354.             else
  355.              x1 = FIXED_MAX;
  356.            }
  357.  
  358.            if (outcode2 == 1) {
  359.             px2 = -v->view_plane_size;
  360.             y_diff = y2 - fixmul(x2, -v->view_plane_size);
  361.             slope += v->view_plane_size;
  362.             if (FIXED_ABS(slope) > FIXED_EPSILON)
  363.              x2 -= fixdiv(y_diff, slope);
  364.             else
  365.              x2 = FIXED_MAX;
  366.            } else if (outcode2 == 2) {
  367.             px2 = v->view_plane_size;
  368.             y_diff = y2 - fixmul(x2, v->view_plane_size);
  369.             slope -= v->view_plane_size;
  370.             if (FIXED_ABS(slope) > FIXED_EPSILON)
  371.              x2 -= fixdiv(y_diff, slope);
  372.             else
  373.              x2 = FIXED_MAX;
  374.            }
  375.       }
  376.  
  377.       add_wall_events(v, wall, x1, px1, x2, px2);
  378.      }
  379. }
  380.  
  381.  
  382. /* Add a wall to the event list--one event is added for the start of the
  383. **   wall, and another is added to mark the end of the wall.
  384. */
  385. static void add_wall_events(View *v, Wall *wall,
  386.                 fixed x1, fixed px1, fixed x2, fixed px2)
  387. {
  388.      int fb1, fb2;
  389.      fixed z1, z2;
  390.      Wall_start *event;
  391.  
  392.  
  393.      /* convert to frame buffer coordinates */
  394.      px1 = fixdiv(px1, view_constants.screen_dx + 1);
  395.      px2 = fixdiv(px2, view_constants.screen_dx + 1);
  396.      fb1 = FIXED_TO_INT(px1) + (VIEW_WIDTH >> 1);
  397.      fb2 = FIXED_TO_INT(px2) + (VIEW_WIDTH >> 1);
  398.  
  399.      /* There's no need to deal with walls that start and end in the same
  400.      **   screen column.  In a properly contructed world, we're guaranteed
  401.      **   that throwing them away won't leave any gaps.
  402.      */
  403.      if (fb1 == fb2)
  404.       return;
  405.  
  406.      /* Here we use a 2.30 fixed point format.  The result of this calculation
  407.      **   is always between 1 and zero, as the distance can never be less
  408.      **   than the view plane distance.  The extra fractional bits are
  409.      **   critical for the inverses.  Note that using 2.30 restricts the
  410.      **   size of the view plane to something less than 2.
  411.      */
  412.      z1 = fixdiv(TO_FIX_2_30(v->eye_distance), x1);
  413.      z2 = fixdiv(TO_FIX_2_30(v->eye_distance), x2);
  414.  
  415.      if (fb1 < fb2) {
  416.  
  417.       event = &start_events[fb1].events[start_events[fb1].n_events];
  418.  
  419.       event->wall = wall;
  420.       event->z = z1;
  421.       event->dz = fixdiv(z2 - z1, INT_TO_FIXED(fb2 - fb1));
  422.       event->is_back_view = False;
  423.       start_events[fb1].n_events++;
  424.  
  425.       end_events[fb2].events[end_events[fb2].n_events] = wall;
  426.       end_events[fb2].n_events++;
  427.  
  428.      } else {
  429.  
  430.       event = &start_events[fb2].events[start_events[fb2].n_events];
  431.  
  432.       event->wall = wall;
  433.       event->z = z2;
  434.       event->dz = fixdiv(z1 - z2, INT_TO_FIXED(fb1 - fb2));
  435.       event->is_back_view = True;
  436.       start_events[fb2].n_events++;
  437.  
  438.       end_events[fb1].events[end_events[fb1].n_events] = wall;
  439.       end_events[fb1].n_events++;
  440.  
  441.      }
  442. }
  443.  
  444.  
  445. static void render_walls(World *w, View *v)
  446. {
  447.      static int last_wall_count = 0;
  448.      static Active_wall *active;
  449.      int column;
  450.      int active_count = 0;
  451.      fixed Vx, Vy, dVy;
  452.      Wall_intersection *cur_slice;
  453.  
  454.  
  455.      /* Make sure that the active list is large enough to hold all the
  456.      **   walls in a world.
  457.      */
  458.      if (last_wall_count != TABLE_SIZE(w->walls)) {
  459.       last_wall_count = TABLE_SIZE(w->walls);
  460.       if (active == NULL)
  461.            active = wtmalloc(sizeof(Active_wall) * last_wall_count);
  462.       else
  463.            active = wtrealloc(active, sizeof(int) * last_wall_count);
  464.  
  465.       /* I know this is really cheesy, but . . . */
  466.       if (intersections == NULL)
  467.            intersections = wtmalloc(sizeof(Wall_intersection) * 3000);
  468.       else
  469.            intersections = wtrealloc(intersections,
  470.                      sizeof(Wall_intersection) * 4000);
  471.      }
  472.  
  473.      /* Set up for fast calculation of view rays. */
  474.      Vx = v->eye_distance;
  475.      Vy = -v->view_plane_size;
  476.      dVy = fixdiv(fixmul(v->view_plane_size, INT_TO_FIXED(2)),
  477.                   INT_TO_FIXED(VIEW_WIDTH));
  478.  
  479.      /* Build a sorted wall intersection list for each screen column. */
  480.      cur_slice = intersections;
  481.  
  482.      for (column = 0; column < VIEW_WIDTH; column++) {
  483.       Active_wall *current, *last;
  484.       /* Keep track of distances of walls in the active list.  Notice that
  485.       **   we're not actually tracking the distances of walls, but
  486.       **   1 / distance instead.  That's because we can linearly
  487.       **   interpolate 1 / distance.  Also, most calculations that
  488.       **   use distance are really using 1 / distance (i.e. distance
  489.       **   appears in the denominator.
  490.       */
  491.  
  492.       active_count = add_events(active, active_count, column);
  493.       cur_slice = calc_wall_heights(active, active_count, column,
  494.                     v->height, cur_slice);
  495.       active_count = remove_events(active, active_count, column);
  496.  
  497.       last = active + active_count - 1;
  498.       for (current = active; current <= last; current++) {
  499.            current->z += current->dz;
  500.            if (current->visible) {
  501.             current->pstart1 += current->dpstart1;
  502.             current->pend1 += current->dpend1;
  503.             current->pstart2 += current->dpstart2;
  504.             current->pend2 += current->dpend2;
  505.            }
  506.       }
  507.  
  508.       Vy += dVy;
  509.      }
  510.  
  511.      /* Once we have the per-column sorted intersection list, most of the
  512.      **   tricky stuff is done.  All that's left is to actually blast bytes
  513.      **   into the framebuffer using the routines in slice.c.
  514.      */
  515.      draw_walls(intersections, v);
  516.      draw_floors(intersections, v);
  517. }
  518.  
  519.  
  520. /* Add new walls to the active list.  The active list is kept
  521. **   depth sorted.  We have to be careful here.  Correct depth
  522. **   ordering of the walls is vital for rendering.  At corners
  523. **   we have two or more walls at the same distance; however,
  524. **   there is still a correct and incorrect ordering.  If one
  525. **   wall is obscured by another, the visible wall must be placed
  526. **   in front in the list.
  527. */
  528. static int add_events(Active_wall *active, int n_active, int column)
  529. {
  530.      int i, j;
  531.      Wall_start *event;
  532.      Wall *wall;
  533.      fixed z;
  534.  
  535.      for (i = 0; i < start_events[column].n_events; i++) {
  536.       event = start_events[column].events + i;
  537.  
  538.       wall = event->wall;
  539.       z = event->z;
  540.  
  541.       for (j = 0; j < n_active; j++) {
  542.            Wall *wall2 = active[j].wall;
  543.            Vertex *common, *v1, *v2;
  544.  
  545.            if (z < active[j].z - MAX_ERROR)
  546.             continue;
  547.            else if (z > active[j].z + MAX_ERROR)
  548.             break;
  549.  
  550.            /* See if the walls share a vertex. */
  551.            if (wall->vertex1 == wall2->vertex1) {
  552.             common = wall->vertex1;
  553.             v1 = wall->vertex2;
  554.             v2 = wall2->vertex2;
  555.            } else if (wall->vertex1 == wall2->vertex2) {
  556.             common = wall->vertex1;
  557.             v1 = wall->vertex2;
  558.             v2 = wall2->vertex1;
  559.            } else if (wall->vertex2 == wall2->vertex1) {
  560.             common = wall->vertex2;
  561.             v1 = wall->vertex1;
  562.             v2 = wall2->vertex2;
  563.            } else if (wall->vertex2 == wall2->vertex2) {
  564.             common = wall->vertex2;
  565.             v1 = wall->vertex1;
  566.             v2 = wall2->vertex1;
  567.            } else {
  568.             /* We have two walls which are really close
  569.             **   together, but share no vertices.  Because
  570.             **   of roundoff error, we don't know for certain
  571.             **   which one is really in front.  Ideally, this
  572.             **   situation will be avoided by creating worldfiles
  573.             **   which don't place non-adjoining walls extremely
  574.             **   close together.
  575.             */
  576.             if (z > active[j].z)
  577.              break;
  578.             else
  579.              continue;
  580.            }
  581.  
  582.            if (!wall_obscured(common, v1, v2) &&
  583.            wall_obscured(common, v2, v1))
  584.             break;
  585.       }
  586.  
  587.       /* Insert the wall into the active list. */
  588.       memmove(active + j + 1, active + j,
  589.           sizeof(Active_wall) * (n_active - j));
  590.       active[j].wall = wall;
  591.       active[j].z = z;
  592.       active[j].dz = event->dz;
  593.       active[j].visible = False;
  594.       active[j].is_back_view = event->is_back_view;
  595.       n_active++;
  596.      }
  597.  
  598.      return n_active;
  599. }
  600.  
  601.  
  602. /* Determine whether wall 1 is obscured by wall 2 from the view point.
  603. **   This will be the case if a halfplane defined by wall 1 contains both the
  604. **   view point and wall 2.  Wall 1 is defined by the points common and v1;
  605. **   wall2 is defined by command and v2.  Note that this function uses the
  606. **   transformed coordinates of the vertices, so the view point and view
  607. **   direction need not be passed explicitly.
  608. */
  609. static Boolean wall_obscured(Vertex *common, Vertex *v1, Vertex *v2)
  610. {
  611.      fixed x1, y1, x2, y2;
  612.      unsigned int sign1, sign2;
  613.  
  614.  
  615.      x1 = common->tx - v1->tx;
  616.      y1 = common->ty - v1->ty;
  617.      x2 = common->tx - v2->tx;
  618.      y2 = common->ty - v2->ty;
  619.  
  620.      /* There's some tricky stuff done here to try to find the signs of
  621.      **   cross products without actually doing any multiplication.  I'm
  622.      **   really not sure if avoiding a few multiplies is worth the extra
  623.      **   overhead of sign checking, but the profiler shows this function as
  624.      **   taking a surprisingly small percentage of execution time.
  625.      */
  626.      if (FIXED_PRODUCT_SIGN(x1, y2) ^ FIXED_PRODUCT_SIGN(x2, y1))
  627.       sign1 = FIXED_PRODUCT_SIGN(x1, y2);
  628.      else
  629.       sign1 = FIXED_SIGN(fixmul(x1, y2) - fixmul(x2, y1));
  630.      if (FIXED_PRODUCT_SIGN(x1, common->ty) ^
  631.      FIXED_PRODUCT_SIGN(common->tx, y1))
  632.       sign2 = FIXED_PRODUCT_SIGN(x1, common->ty);
  633.      else
  634.       sign2 = FIXED_SIGN(fixmul(x1, common->ty) - fixmul(common->tx, y1));
  635.  
  636.      if (sign1 ^ sign2)
  637.       return False;
  638.      else
  639.       return True;
  640. }
  641.  
  642.  
  643. /* Remove walls from the active list.  Return the number of remaining
  644. ** active walls.
  645. */
  646. static int remove_events(Active_wall *active, int n_active, int column)
  647. {
  648.      int i, j;
  649.      Wall *wall;
  650.  
  651.  
  652.      /* This is really inefficient, so I hope these event lists are small. */
  653.      for (i = 0; i < end_events[column].n_events; i++) {
  654.       wall = end_events[column].events[i];
  655.  
  656.       for (j = 0; j < n_active && active[j].wall != wall; j++);
  657.       n_active--;
  658.       memmove(active + j, active + j + 1,
  659.           sizeof(Active_wall) * (n_active - j));
  660.      }
  661.  
  662.      return n_active;
  663. }
  664.  
  665.  
  666. static Wall_intersection *calc_wall_heights(Active_wall *active, int n_active,
  667.                         int column, fixed height,
  668.                         Wall_intersection *cur_slice)
  669. {
  670.      fixed top = FIXED_ONE_HALF, bottom = -FIXED_ONE_HALF;
  671.  
  672.  
  673.      while (n_active-- > 0 && bottom < top) {
  674.       Wall *wall = active->wall;
  675.       fixed z = active->z;
  676.       fixed dz = active->dz;
  677.  
  678.       if (!active->visible) {
  679.            active->pstart1 =
  680.             fixmul2_30(TO_FIX_8_24(wall->front->floor - height), z);
  681.            active->pend1 =
  682.             fixmul2_30(TO_FIX_8_24(wall->back->floor - height), z);
  683.            active->pstart2 =
  684.             fixmul2_30(TO_FIX_8_24(wall->back->ceiling - height), z);
  685.            active->pend2 =
  686.             fixmul2_30(TO_FIX_8_24(wall->front->ceiling - height), z);
  687.            active->dpstart1 =
  688.             fixmul2_30(TO_FIX_8_24(wall->front->floor - height), dz);
  689.            active->dpend1 =
  690.             fixmul2_30(TO_FIX_8_24(wall->back->floor - height), dz);
  691.            active->dpstart2 =
  692.             fixmul2_30(TO_FIX_8_24(wall->back->ceiling - height), dz);
  693.            active->dpend2 =
  694.             fixmul2_30(TO_FIX_8_24(wall->front->ceiling - height), dz);
  695.            active->visible = True;
  696.       }
  697.       cur_slice->pstart1 = FROM_FIX_8_24(active->pstart1);
  698.       cur_slice->pend1 = FROM_FIX_8_24(active->pend1);
  699.       cur_slice->pstart2 = FROM_FIX_8_24(active->pstart2);
  700.       cur_slice->pend2 = FROM_FIX_8_24(active->pend2);
  701.  
  702.       if (active->is_back_view) {
  703.            cur_slice->front = wall->back;
  704.            cur_slice->back = wall->front;
  705.            cur_slice->pstart1 = cur_slice->pend1;
  706.            cur_slice->pend2 = cur_slice->pstart2;
  707.       } else {
  708.            cur_slice->front = wall->front;
  709.            cur_slice->back = wall->back;
  710.       }
  711.  
  712.       cur_slice->top = top;
  713.       cur_slice->bottom = bottom;
  714.       cur_slice->wall = wall;
  715.       cur_slice->z = FROM_FIX_2_30(z);
  716.       cur_slice->is_back_view = active->is_back_view;
  717.  
  718.       if (cur_slice->is_back_view) {
  719.            if (bottom < cur_slice->pstart1)
  720.             bottom = cur_slice->pstart1;
  721.            if (top > cur_slice->pend2)
  722.             top = cur_slice->pend2;
  723.       } else {
  724.            if (bottom < cur_slice->pend1)
  725.             bottom = cur_slice->pend1;
  726.            if (top > cur_slice->pstart2)
  727.             top = cur_slice->pstart2;
  728.       }
  729.  
  730.       active++;
  731.       cur_slice++;
  732.      }
  733.  
  734.      /* place the 'end of column' sentinel */
  735.      cur_slice->wall = NULL;
  736.      cur_slice->top = top;
  737.      cur_slice->bottom = bottom;
  738.  
  739.      cur_slice++;
  740.  
  741.      return cur_slice;
  742. }
  743.  
  744.  
  745. static void draw_walls(Wall_intersection *int_list, View *v)
  746. {
  747.      int i = 0;
  748.      int fb_column = VIEW_WIDTH - 1;
  749.      unsigned int tex_column, tex_mask;
  750.      unsigned char *tex_base;
  751.      fixed tex_y, tex_dy;
  752.      Pixel *fb_byte, *last_byte;
  753.      fixed pstart1, pend1, pstart2, pend2;
  754.      fixed start1, start2;
  755.      fixed Vx, Vy, dVy;
  756.      fixed height = v->height;
  757.  
  758.  
  759.      Vx = v->eye_distance;
  760.      Vy = -v->view_plane_size;
  761.      dVy = fixdiv(FIXED_DOUBLE(v->view_plane_size), INT_TO_FIXED(VIEW_WIDTH));
  762.  
  763.      while (i < VIEW_WIDTH) {
  764.       Wall *wall = int_list->wall;
  765.       Texture *texture;
  766.       fixed z, t;
  767.       Boolean do_floor, do_ceiling;
  768.  
  769.       if (wall == NULL) {
  770.            int_list++;
  771.            i++;
  772.            fb_column--;
  773.            Vy += dVy;
  774.            continue;
  775.       }
  776.  
  777.       if (int_list->is_back_view) {
  778.            start1 = int_list->back->floor;
  779.            start2 = int_list->front->ceiling;
  780.            pstart1 = int_list->pend1;
  781.            pend1 = int_list->pstart1;
  782.            pstart2 = int_list->pend2;
  783.            pend2 = int_list->pstart2;
  784.       } else {
  785.            start1 = int_list->front->floor;
  786.            start2 = int_list->back->ceiling;
  787.            pstart1 = int_list->pstart1;
  788.            pend1 = int_list->pend1;
  789.            pstart2 = int_list->pstart2;
  790.            pend2 = int_list->pend2;
  791.       }
  792.       if (pend1 > pend2)
  793.            pend1 = pend2;
  794.       if (pstart2 < pstart1)
  795.            pstart2 = pstart1;
  796.  
  797.  
  798.       texture = wall->surface_texture;
  799.       z = int_list->z;
  800.       do_floor = (pstart1 < int_list->top) &&
  801.                  (pend1 > int_list->bottom) &&
  802.              (pend1 - int_list->pstart1 > FIXED_EPSILON);
  803.       do_ceiling = (pstart2 < int_list->top) &&
  804.                    (pend2 > int_list->bottom) &&
  805.                (pend2 - int_list->pstart2 > FIXED_EPSILON);
  806.  
  807.       /* Don't do anything more with this wall if there's nothing
  808.       **   to draw.
  809.       */
  810.       if (!do_floor && !do_ceiling) {
  811.            int_list++;
  812.            continue;
  813.       }
  814.  
  815.       if (wall->sky) {
  816.            fixed angle;
  817.  
  818.            /* Compute the angle of this column.  We'll use the angle
  819.            **   exclusively to determine which texture column to
  820.            **   display for this slice of sky--that's the way sky
  821.            **   works.
  822.            */
  823.            angle = v->angle +
  824.             fixdiv(FIXED_SCALE(v->arc, i - (VIEW_WIDTH >> 1)),
  825.                INT_TO_FIXED(VIEW_WIDTH));
  826.            angle -= FIXED_SCALE(FIXED_2PI,
  827.                     FIXED_TO_INT(fixdiv(angle, FIXED_2PI)));
  828.            angle = fixdiv(angle, FIXED_2PI);
  829.            if (angle < FIXED_ZERO)
  830.             angle = FIXED_ONE - angle;
  831.            tex_column = FIXED_TO_INT(fixmul(angle, wall->xscale)) &
  832.             (texture->width - 1);
  833.            tex_dy = fixdiv(wall->yscale, INT_TO_FIXED(VIEW_HEIGHT));
  834.       } else {
  835.            t = wall_ray_intersection(Vx, Vy, wall);
  836.            /* From t, calculate the integer coordinates in the texture
  837.         **   bitmap.  For efficiency, we assume that the width of the
  838.         **   texture is a power of two.
  839.         */
  840.            tex_column =
  841.             FIXED_TO_INT(wall->xphase + fixmul(t, wall->xscale)) &
  842.              (texture->width - 1);
  843.            /* Test to avoid overflow here . . .  if the wall is so far
  844.         **   away that z (which is 1 / distance) is less than
  845.         **   FIXED_EPSILON, then it will be so small when rendered
  846.         **   that we can use a bogus value for tex_dy.
  847.         */
  848.            if (z < FIXED_EPSILON)
  849.             tex_dy = 0;
  850.            else
  851.             tex_dy = fixdiv(wall->yscale, FIXED_SCALE(z, VIEW_HEIGHT));
  852.       }
  853.       tex_mask = texture->height - 1;
  854.       tex_base = texture->texels + (tex_column << texture->log2width);
  855.  
  856.       /* Now we can actually draw the walls, starting with
  857.       **   the floor segment.
  858.       */
  859.       if (do_floor) {
  860.            int fb_start, fb_end;
  861.  
  862.            if (pstart1 < int_list->bottom) {
  863.             pstart1 = int_list->bottom;
  864.             start1 = fixdiv(pstart1, z) + height;
  865.            }
  866.            if (pend1 > int_list->top)
  867.             pend1 = int_list->top;
  868.  
  869.            fb_start = FIXED_TO_INT(FIXED_SCALE(pstart1, VIEW_HEIGHT));
  870.            fb_end = FIXED_TO_INT(FIXED_SCALE(pend1, VIEW_HEIGHT));
  871.  
  872.            fb_start = (VIEW_HEIGHT >> 1) - fb_start;
  873.            /* The following line of code is a hack . . . */
  874.            if (fb_start >= VIEW_HEIGHT)
  875.             fb_start = VIEW_HEIGHT - 1;
  876.            fb_end = (VIEW_HEIGHT >> 1) - fb_end;
  877.            fb_byte = fb->pixels + fb_column + fb_rows[fb_start];
  878.            last_byte = fb->pixels + fb_column + fb_rows[fb_end];
  879.            if (wall->sky)
  880.             tex_y = fixmul(view_constants.row_view[fb_start],
  881.                    wall->yscale);
  882.            else
  883.             tex_y = fixmul(start1, wall->yscale) + wall->yphase;
  884.  
  885.            draw_wall_slice(fb_byte, last_byte, tex_base,
  886.                    tex_y, tex_dy, VIEW_WIDTH, texture->height,
  887.                    fb_start - fb_end);
  888.       }
  889.  
  890.       /* Draw the ceiling segment. */
  891.       if (do_ceiling) {
  892.            int fb_start, fb_end;
  893.  
  894.            if (pstart2 < int_list->bottom) {
  895.             pstart2 = int_list->bottom;
  896.             start2 = fixdiv(pstart2, z) + height;
  897.            }
  898.            if (pend2 > int_list->top)
  899.             pend2 = int_list->top;
  900.  
  901.            fb_start = FIXED_TO_INT(FIXED_SCALE(pstart2, VIEW_HEIGHT));
  902.            fb_end = FIXED_TO_INT(FIXED_SCALE(pend2, VIEW_HEIGHT));
  903.  
  904.            fb_start = (VIEW_HEIGHT >> 1) - fb_start;
  905.            /* The following line of code is a hack . . . */
  906.            if (fb_start >= VIEW_HEIGHT)
  907.             fb_start = VIEW_HEIGHT - 1;
  908.            fb_end = (VIEW_HEIGHT >> 1) - fb_end;
  909.            fb_byte = fb->pixels + fb_column + fb_rows[fb_start];
  910.            last_byte = fb->pixels + fb_column + fb_rows[fb_end];
  911.            if (wall->sky)
  912.             tex_y = view_constants.row_view[fb_start];
  913.            else
  914.             tex_y = fixmul(start2, wall->yscale) + wall->yphase;
  915.  
  916.            draw_wall_slice(fb_byte, last_byte, tex_base,
  917.                    tex_y, tex_dy, VIEW_WIDTH, texture->height,
  918.                    fb_start - fb_end);
  919.       }
  920.  
  921.       int_list++;
  922.      }
  923. }
  924.  
  925.  
  926. static void draw_floors(Wall_intersection *int_list, View *v)
  927. {
  928.      int i;
  929.      Wall_intersection *last, *current;
  930.      Wall_intersection *save_current, *save_last;
  931.      fixed start, end;
  932.      fixed max_floor, min_ceiling;
  933.      fixed last_max_floor = FIXED_ZERO, last_min_ceiling = FIXED_ZERO;
  934.  
  935.  
  936.      last = current = int_list;
  937.  
  938.      for (i = 1; i < VIEW_WIDTH; i++) {
  939.  
  940.       for (; current->wall != NULL; current++);
  941.       save_current = ++current;
  942.       save_last = last;
  943.  
  944.       /* get the highest floor and lowest ceiling */
  945.       while (current->wall != NULL)
  946.            current++;
  947.       max_floor = current->bottom;
  948.       min_ceiling = current->top;
  949.  
  950.       current = save_current;
  951.  
  952.       /* draw floors */
  953.       while (last->wall != NULL && current->wall != NULL) {
  954.  
  955.            if (current->front == last->front)
  956.             start = MAX(last->bottom, current->pstart1);
  957.            else
  958.             start = MAX(last->bottom, current->bottom);
  959.            end = MIN(last->pstart1, current->pend1);
  960.  
  961.            if (start < end)
  962.             draw_floor_slices(last->front, start, end, v, i - 1);
  963.  
  964.            if (last->pend1 < current->pend1)
  965.             last++;
  966.            else
  967.             current++;
  968.       }
  969. /*
  970.       while (last->wall != NULL) {
  971.            end = last->bottom;
  972.            start = MAX(min_ceiling, last->pstart1);
  973.            if (start < end)
  974.             draw_floor_slices(last->front, start, end, v, i - 1);
  975.            last++;
  976.       }
  977. */
  978.       /* draw ceiling slices generated by ceiling walls */
  979.       last = save_last;
  980.       current = save_current;
  981.       while (last->wall != NULL && current->wall != NULL) {
  982.  
  983.            if (current->front == last->front)
  984.             end = MIN(last->top, current->pend2);
  985.            else
  986.             end = MIN(last->top, current->top);
  987.            start = MAX(last->pend2, current->pstart2);
  988.  
  989.            if (start < end)
  990.             draw_ceiling_slices(last->front, start, end, v, i - 1);
  991.  
  992.            if (last->pstart2 > current->pstart2)
  993.             last++;
  994.            else
  995.             current++;
  996.       }
  997.  
  998.       /* draw ceiling slices generated by floor walls */
  999.       while (last->wall != NULL) {
  1000.            start = MAX(last_max_floor, last->pend2);
  1001.            end = MIN(max_floor, last->top);
  1002.            if (start < end)
  1003.             draw_ceiling_slices(last->front, start, end, v, i - 1);
  1004.            last++;
  1005.       }
  1006.       last = save_current;
  1007.  
  1008.       last_max_floor = max_floor;
  1009.       last_min_ceiling = min_ceiling;
  1010.      }
  1011.  
  1012.      save_last = last;
  1013.  
  1014.      /* finish up the floors */
  1015.      while (last->wall != NULL) {
  1016.  
  1017.       start = MAX(last->bottom, -FIXED_ONE_HALF);
  1018.       end = MIN(last->pstart1, FIXED_ZERO);
  1019.  
  1020.       if (start < end)
  1021.            draw_floor_slices(last->front, start, end, v, i - 1);
  1022.  
  1023.       last++;
  1024.      }
  1025.  
  1026.      /* finish up the ceilings */
  1027.      last = save_last;
  1028.      while (last->wall != NULL) {
  1029.  
  1030.       start = MAX(last->pend2, FIXED_ZERO);
  1031.       end = MIN(last->top, FIXED_ONE_HALF);
  1032.  
  1033.       if (start < end)
  1034.            draw_ceiling_slices(last->front, start, end, v, i - 1);
  1035.  
  1036.       last++;
  1037.      }
  1038.  
  1039. }
  1040.  
  1041.  
  1042. static void draw_floor_slices(Region *r, fixed start, fixed end,
  1043.                   View *v, int column)
  1044. {
  1045.      int screen_column = VIEW_WIDTH - column - 1;
  1046.      int start_row, end_row;
  1047.      Texture *texture = r->floor_tex;
  1048.      fixed height;
  1049.      fixed sin_x, cos_x;
  1050.  
  1051.  
  1052.      start_row = FIXED_TO_INT(start * VIEW_HEIGHT);
  1053.      end_row = FIXED_TO_INT(end * VIEW_HEIGHT);
  1054.      /* Bail out early and save time if there's nothing to draw. */
  1055.      if (start_row == end_row)
  1056.       return;
  1057.  
  1058.      start_row = (VIEW_HEIGHT >> 1) - start_row;
  1059.      end_row = (VIEW_HEIGHT >> 1) - end_row;
  1060.      if (start_row >= VIEW_HEIGHT)
  1061.       start_row = VIEW_HEIGHT - 1;
  1062.      if (end_row < 0)
  1063.       end_row = 0;
  1064.  
  1065.      height = r->floor - v->height;
  1066.      sin_x = view_constants.sin_tab[column];
  1067.      cos_x = view_constants.cos_tab[column];
  1068.  
  1069.  
  1070.      while (start_row >= end_row) {
  1071.       fixed x, dx, y, dy;
  1072.       fixed y1;
  1073.       Pixel *fb_byte = fb->pixels + fb_rows[start_row] + screen_column;
  1074.  
  1075.       if (FIXED_ABS(view_constants.row_view[start_row]) < FIXED_EPSILON)
  1076.            y1 = FIXED_ONE;
  1077.       else
  1078.            y1 = fixdiv(height, view_constants.row_view[start_row]) << 4;
  1079.  
  1080.       x = fixmul(view_constants.view_sin - cos_x, y1) - (v->y << 4);
  1081.       y = fixmul(-view_constants.view_cos - sin_x, y1) - (v->x << 4);
  1082.       dx = fixmul(view_constants.cos_dx, y1);
  1083.       dy = fixmul(view_constants.sin_dx, y1);
  1084.  
  1085.       draw_floor_slice(fb_byte, texture->texels, x, y, dx, dy,
  1086.                texture->width);
  1087.  
  1088.       start_row--;
  1089.      }
  1090. }
  1091.  
  1092.  
  1093. static void draw_ceiling_slices(Region *r, fixed start, fixed end,
  1094.                 View *v, int column)
  1095. {
  1096.      int screen_column = VIEW_WIDTH - column - 1;
  1097.      int start_row, end_row;
  1098.      Texture *texture = r->ceiling_tex;
  1099.      fixed height;
  1100.      fixed sin_x, cos_x;
  1101.  
  1102.  
  1103.      start_row = FIXED_TO_INT(start * VIEW_HEIGHT);
  1104.      end_row = FIXED_TO_INT(end * VIEW_HEIGHT);
  1105.      /* Bail out early and save time if there's nothing to draw. */
  1106.      if (start_row == end_row)
  1107.       return;
  1108.  
  1109.      start_row = (VIEW_HEIGHT >> 1) - start_row;
  1110.      end_row = (VIEW_HEIGHT >> 1) - end_row;
  1111.      if (start_row >= VIEW_HEIGHT)
  1112.       start_row = VIEW_HEIGHT - 1;
  1113.      if (end_row < 0)
  1114.       end_row = 0;
  1115.  
  1116.      height = r->ceiling - v->height;
  1117.      sin_x = view_constants.sin_tab[column];
  1118.      cos_x = view_constants.cos_tab[column];
  1119.  
  1120.  
  1121.      while (start_row >= end_row) {
  1122.       fixed x, dx, y, dy;
  1123.       fixed y1;
  1124.       Pixel *fb_byte = fb->pixels + fb_rows[start_row] + screen_column;
  1125.  
  1126.       if (FIXED_ABS(view_constants.row_view[start_row]) < FIXED_EPSILON)
  1127.            y1 = FIXED_ONE;
  1128.       else
  1129.            y1 = fixdiv(height, view_constants.row_view[start_row]) << 4;
  1130.  
  1131.       x = fixmul(view_constants.view_sin - cos_x, y1) - (v->y << 4);
  1132.       y = fixmul(-view_constants.view_cos - sin_x, y1) - (v->x << 4);
  1133.       dx = fixmul(view_constants.cos_dx, y1);
  1134.       dy = fixmul(view_constants.sin_dx, y1);
  1135.  
  1136.       draw_floor_slice(fb_byte, texture->texels, x, y, dx, dy,
  1137.                texture->width);
  1138.  
  1139.       start_row--;
  1140.      }
  1141. }
  1142.  
  1143.  
  1144. /* Calculate the value of the parameter t at the intersection
  1145. **   of the view ray and the wall.  t is 0 at the origin of
  1146. **   the wall, and 1 at the other endpoint.
  1147. */
  1148. static fixed wall_ray_intersection(fixed Vx, fixed Vy, Wall *wall)
  1149. {
  1150.      fixed denominator, Nx, Ny, Wx, Wy;
  1151.  
  1152.      Nx = -Vy;
  1153.      Ny = Vx;
  1154.      Wx = wall->vertex2->tx - wall->vertex1->tx;
  1155.      Wy = wall->vertex2->ty - wall->vertex1->ty;
  1156.  
  1157.      denominator = fixmul(Nx, Wx) + fixmul(Ny, Wy); /* N dot W */
  1158.      if (denominator < FIXED_EPSILON)
  1159.       return FIXED_ONE - fixdiv(fixmul(Nx, wall->vertex1->tx) +
  1160.                     fixmul(Ny, wall->vertex1->ty),
  1161.                     -denominator);
  1162.      else if (denominator > FIXED_EPSILON)
  1163.       return fixdiv(fixmul(Nx, wall->vertex1->tx) +
  1164.             fixmul(Ny, wall->vertex1->ty),
  1165.             -denominator);
  1166.      else
  1167.       return FIXED_ZERO;
  1168. }
  1169.  
  1170.  
  1171. static void init_buffers(void)
  1172. {
  1173.      int i;
  1174.  
  1175.      for (i = 0; i < VIEW_WIDTH + 1; i++) {
  1176.       start_events[i].n_events = 0;
  1177.       end_events[i].n_events = 0;
  1178.      }
  1179. }
  1180.  
  1181.  
  1182. /* Calculate values that are dependent only on the screen dimensions and
  1183. **   the view.
  1184. */
  1185. static void calc_view_constants(View *v, int screen_width, int screen_height)
  1186. {
  1187.      static int last_height = 0, last_width = 0;
  1188.      int i;
  1189.      fixed x, y;
  1190.  
  1191.  
  1192.      /* Make sure that enough memory has been allocated for the tables. */
  1193.      if (screen_height != last_height) {
  1194.       if (last_height == 0)
  1195.            view_constants.row_view =
  1196.             wtmalloc(screen_height * sizeof(fixed));
  1197.       else
  1198.            view_constants.row_view =
  1199.             wtrealloc(view_constants.row_view,
  1200.                  screen_height * sizeof(fixed));
  1201.       last_height = screen_height;
  1202.      }
  1203.      if (screen_width != last_width) {
  1204.       if (last_width == 0) {
  1205.            view_constants.sin_tab = wtmalloc(screen_width * sizeof(fixed));
  1206.            view_constants.cos_tab = wtmalloc(screen_width * sizeof(fixed));
  1207.       } else {
  1208.            view_constants.sin_tab =
  1209.             wtrealloc(view_constants.sin_tab,
  1210.                   screen_width * sizeof(fixed));
  1211.            view_constants.cos_tab =
  1212.             wtrealloc(view_constants.cos_tab,
  1213.                   screen_width * sizeof(fixed));
  1214.       }
  1215.       last_width = screen_width;
  1216.      }
  1217.  
  1218.      view_constants.view_sin =
  1219.       FLOAT_TO_FIXED(sin(- FIXED_TO_FLOAT(v->angle)));
  1220.      view_constants.view_cos =
  1221.       FLOAT_TO_FIXED(cos(- FIXED_TO_FLOAT(v->angle)));
  1222.      view_constants.screen_dx = fixdiv(FIXED_DOUBLE(v->view_plane_size),
  1223.                        INT_TO_FIXED(screen_width));
  1224.      view_constants.screen_dy = fixdiv(FIXED_ONE,
  1225.                        INT_TO_FIXED(screen_height));
  1226.      view_constants.sin_dx = fixmul(view_constants.view_sin,
  1227.                     view_constants.screen_dx);
  1228.      view_constants.cos_dx = fixmul(view_constants.view_cos,
  1229.                     view_constants.screen_dx);
  1230.      y = FIXED_SCALE(view_constants.sin_dx, -(screen_width >> 1));
  1231.      x = FIXED_SCALE(view_constants.cos_dx, -(screen_width >> 1));
  1232.      for (i = 0; i < screen_width; i++) {
  1233.       view_constants.sin_tab[i] = y;
  1234.       view_constants.cos_tab[i] = x;
  1235.       y += view_constants.sin_dx;
  1236.       x += view_constants.cos_dx;
  1237.      }
  1238.  
  1239.      y = FIXED_SCALE(view_constants.screen_dy, screen_height >> 1);
  1240.      for (i = 0; i < screen_height; i++) {
  1241.       view_constants.row_view[i] = y;
  1242.       y -= view_constants.screen_dy;
  1243.      }
  1244. }
  1245.